#include "lang.h"

static void outputPreamble(FILE *f)
{
    fputs("#include \"tree-dump.h\"\n"
          "#include \"tree-dump-primitives.h\"\n"
          "\n"
          "static void __tree_dump(tree t, int depth);\n"
          "\n"
          "static void tree_print_indent(int depth)\n"
          "{\n"
          "    int i;\n"
          "    for (i = 0; i < depth *2; i++)\n"
          "        fprintf(stderr, \" \");\n"
          "}\n"
          "\n"
          "static void __tree_dump_1(tree head, int depth)\n"
          "{\n"
          "    switch(TYPE(head)) {\n", f);
}

static void outputTreeType(FILE *f, const struct TreeType &type)
{
    bool isFirstProp = true;
    bool hasPrintedTree = false;

    fprintf(f, "    case %s:\n", type.name.c_str());
    fprintf(f, "        fprintf(stderr, \"\\n\");\n");
    fprintf(f, "        tree_print_indent(depth);\n");
    fprintf(f, "        fprintf(stderr, \"<tree %s\");\n", type.name.c_str());

    for (const auto &prop : type.props) {
        if (isFirstProp)
            isFirstProp = false;
        else {
            fprintf(f, "        fprintf(stderr, \"\\n\");\n");
            fprintf(f, "        tree_print_indent(depth);\n");
            fprintf(f, "        fprintf(stderr, \"     \");\n");
        }

        fprintf(f, "        fprintf(stderr, \" %s: \");\n", type.getPropAccessor(prop.first).c_str());

        if (prop.second.baseType.isTree) {
            hasPrintedTree = true;
            fprintf(f, "        __tree_dump(%s(head), depth + 1);\n", type.getPropAccessor(prop.first).c_str());
        } else
            fprintf(f, "        tree_dump_%s(head, %s(head), TEXTUAL);\n",
                    prop.second.baseType.name.c_str(), type.getPropAccessor(prop.first).c_str());

    }

    if (hasPrintedTree) {
        fprintf(f, "        fprintf(stderr, \"\\n\");\n");
        fprintf(f, "        tree_print_indent(depth);\n");
    }
    fprintf(f, "        fprintf(stderr, \">\");\n");
    fprintf(f, "        break;\n");
}

static void outputDumpTypes(FILE *f, const struct lang &lang)
{
    for (const auto &tt : lang.treeTypes)
        outputTreeType(f, tt);

    for (const auto &ct : lang.treeCTypes)
        outputTreeType(f, ct);
}

static void outputEpilogue(FILE *f)
{
    fputs("    }\n"
          "}\n"
          "\n"
          "static void __tree_dump(tree head, int depth)\n"
          "{\n"
          "    tree i;\n"
          "\n"
          "    if (!head) {\n"
          "        tree_print_indent(depth);\n"
          "        fprintf(stderr, \"<null>\\n\");\n"
          "        return;\n"
          "    }\n"
          "\n"
          "    if (is_CHAIN_HEAD(head)) {\n"
          "        fprintf(stderr, \"\\n\");\n"
          "        tree_print_indent(depth);\n"
          "        fprintf(stderr, \"<chain\");\n"
          "\n"
          "        for_each_tree(i, head)\n"
          "            __tree_dump_1(i, depth+1);\n"
          "\n"
          "        fprintf(stderr, \"\\n\");\n"
          "        tree_print_indent(depth);\n"
          "        fprintf(stderr, \">\");\n"
          "    }\n"
          "    else\n"
          "        __tree_dump_1(head, depth);\n"
          "}\n"
          "\n"
          "void tree_dump(tree head)\n"
          "{\n"
          "    __tree_dump(head, 0);\n"
          "    fprintf(stderr, \"\\n\");\n"
          "}\n", f);
}


static void usage(char *progname)
{
    fprintf(stderr, "%s - Generate the `tree-access.h' file from a language description\n",
            progname);
    fprintf(stderr, "Usage: %s LANG_DESC_FILE\n", progname);
    fprintf(stderr, "\n");
    fprintf(stderr, "Where LANG_DESC_FILE is a language description file.\n");
}

int main(int argc, char *argv[])
{
    struct lang lang;
    FILE *f;

    if (argc != 2) {
        fprintf(stderr, "Error: %s expects exactly one argument\n", argv[0]);
        usage(argv[0]);
        return 1;
    }

    f = fopen(argv[1], "r");

    if (!f) {
        perror("Could not open lang file");
        usage(argv[0]);
        exit(2);
    }

    lang_read(f, lang);

    FILE *output = fopen("tree-dump.c", "w");

    fputs("/*\n"
          " * tree-dump.c - Define the tree_dump function.\n"
          " *\n"
          " * Automatically generated by gendump. DO NOT EDIT.\n"
          " */\n\n", output);

    outputPreamble(output);
    outputDumpTypes(output, lang);
    outputEpilogue(output);

    fclose(output);

    return 0;
}
